﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Threading;
using System.Windows.Forms;
using FreeBroker.Properties;
using MRG.Controls.UI;
using System.Drawing.Imaging;
using System.Text;

namespace FreeBroker
{
    public partial class DetailView : Form
    {
        private BrokerObject broker;

        private Dictionary<int, BrokerObject> brokers;

        private int number;

        public DetailView(BrokerMainForm mainForm, int procNumber)
        {
            InitializeComponent();

            this.brokers = mainForm.Processes;
            this.broker = brokers[procNumber];
            // Set title
            StringBuilder title = new StringBuilder("Detailansicht - ");
            title.Append(Helper.UnescapeHtmlString(this.broker.Name));
            title.Append("  (WKN: ");
            title.Append(this.broker.Wkn);
            title.Append("   |   ISIN: ");
            title.Append(this.broker.Isin);
            title.Append(")");
            this.Text = title.ToString();
            //
            this.loadingCircle.Active = true;
            this.loadingCircle.Color = Color.SlateGray;
            this.loadingCircle.FadeInSpeed = LoadingCircle.FadeSpeeds.Fast;
            this.loadingCircle.FadeOutSpeed = LoadingCircle.FadeSpeeds.Fast;
            this.loadingCircle.InnerCircleRadius = 14;
            this.loadingCircle.Location = new Point(616, 12);
            this.loadingCircle.Name = "loadingCircle";
            this.loadingCircle.NumberSpoke = 80;
            this.loadingCircle.OuterCircleRadius = 24;
            this.loadingCircle.RotationSpeed = 15;
            this.loadingCircle.Size = new Size(53, 57);
            this.loadingCircle.SpokeThickness = 3;
            this.loadingCircle.Visible = false;
            //
            foreach (BrokerObject item in brokers.Values)
            {
                if (item != broker)
                {
                    Color color = RandomHelper.RandomColor();
                    ListViewItem lvItem = new ListViewItem(item.Name);
                    lvItem.BackColor = color;
                    Color negativeColor = Color.FromArgb((255 - color.R) * 65536 + (255 - color.G) * 256 + (255 - color.B));
                    lvItem.ForeColor = negativeColor;
                    this.listViewComparison.Items.Add(lvItem);
                }
            }

            if (broker != null)
            {
                this.number = procNumber;
                StartCircle(this.loadingCircle);
                broker.loadChartOptions(number);
                string request = ChartController.buildRequest(broker);
                Image img = broker.chartDetail;
                if (img == null)
                {
                    backgroundWorker.RunWorkerAsync(request);
                }
                else
                {
                    this.bmpBack = (Bitmap)img;
                    this.image.Image = (Bitmap)bmpBack.Clone();
                    this.image.SendToBack();
                    this.image.Width = img.Width;
                    this.image.Height = img.Height;

                    backgroundWorker.RunWorkerAsync(request);
                }
                broker.ActiveDetailView = this;
            }
            Lines = new ArrayList();

            this.SetStyle(ControlStyles.DoubleBuffer, true);
            this.SetStyle(ControlStyles.UserPaint, true);
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);

            this.colorView.BackColor = Settings.Default.LineColor;

            Redraw();
            createMenues();
            //
            // Set window size
            ChartOptions opt = Config.Broker.GetChartOptionsByNumber(procNumber);
            if (opt != null && opt.WindowWidth > 0 && opt.WindowHeight > 0)
            {
                this.Size = new Size(opt.WindowWidth, opt.WindowHeight);
            }
        }

        private void createMenues()
        {
            Dictionary<string, Dictionary<string, string>>.KeyCollection keys = ChartController.OPTIONS.Keys;
            foreach (string key in keys)
            {
                ToolStripMenuItem menuItem = new ToolStripMenuItem(key);
                menuMapper(ChartController.OPTIONS[key], menuItem);
                contextMenuStripChart.Items.Add(menuItem);
            }
        }

        private void menuMapper(Dictionary<string, string> map, ToolStripMenuItem parent)
        {
            Dictionary<string, string>.KeyCollection keys = map.Keys;
            foreach (string key in keys)
            {
                ToolStripMenuItem item = new ToolStripMenuItem(key, null, new EventHandler(menuItem_Click));
                string currentValue = ChartController.OPTIONS[parent.Text][key];
                if (this.broker.chartOptions.ContainsKey(parent.Text))
                {
                    item.Checked = currentValue.Equals(this.broker.chartOptions[parent.Text]);
                }
                else
                {
                    item.Checked = false;
                }
                
                parent.DropDownItems.Add(item);
            }
        }

        /// <summary>
        ///     A chart option has been selected.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuItem_Click(object sender, EventArgs e)
        {
            ToolStripMenuItem thisItem = (ToolStripMenuItem)sender;
            if (!thisItem.Checked)
            {
                ToolStripMenuItem parent = (ToolStripMenuItem)thisItem.OwnerItem;
                int index = 0;
                for (int i = 0; i < parent.DropDownItems.Count; i++)
                {
                    ToolStripMenuItem item = (ToolStripMenuItem)parent.DropDownItems[i];
                    item.Checked = false;
                    if (thisItem == item)
                    {
                        index = i;
                    }
                }
                thisItem.Checked = true;
                string parentText = parent.Text;
                if (this.broker.chartOptionIndices.ContainsKey(parentText))
                {
                    this.broker.chartOptionIndices[parentText] = index;
                }
                else
                {
                    this.broker.chartOptionIndices.Add(parentText, index);
                }
                
                this.broker.saveChartOptions(this.number);
                ReloadImage();
            }
        }

        private void ReloadImage()
        {
            BrokerObject broker = this.broker;
            string request = ChartController.buildRequest(broker);
            DateTime startTime = DateTime.Now;
            if (backgroundWorker.IsBusy)
            {
                MessageForm waitForm = new MessageForm();
                waitForm.Show();
                backgroundWorker.CancelAsync();
                while (backgroundWorker.IsBusy && (DateTime.Now - startTime).TotalSeconds < 30)
                {
                    Thread.Sleep(500);
                    this.Refresh();
                }
                waitForm.Close();
            }
            if ((DateTime.Now - startTime).TotalSeconds < 30)
            {
                backgroundWorker.RunWorkerAsync(request);
            }
        }


        #region PAINT METHODS


        Bitmap bmpBack = null;
        private bool isSelected = false;
        private int _X, _Y;
        private ArrayList Lines = null;

        private void buttonLine_Click(object sender, EventArgs e)
        {
            //Pen myPen;
            //myPen = new System.Drawing.Pen(Color.Red);
            //Graphics formGraphics = this.CreateGraphics();
            //formGraphics.DrawLine(myPen, 0, 0, 200, 200);
            //myPen.Dispose();
            //formGraphics.Dispose();
            button1_Click(sender, e);
        }

        //New
        private void menuItem17_Click(object sender, System.EventArgs e)
        {
            image.Controls.Clear();
            bmpBack = new Bitmap(image.Width, image.Height);
            image.Image = (Bitmap)bmpBack.Clone();

            Lines.Clear();
        }



        private void button1_Click(object sender, System.EventArgs e)
        {
            try
            {
                //Adds red marks that are the beginning/end of the line
                MarkControl mark1 = new MarkControl();
                Color color = Color.FromArgb(0, 250, 250, 250);
                mark1.ForeColor = color;
                mark1.BackColor = Color.FromArgb(0, 250, 250, 250);
                int width = this.image.Width;
                int height = this.image.Height;
                int posY = height / 2;
                mark1.Location = new Point(20, posY);
                mark1.ContextMenuStrip = this.contextMenuStripMark;
                image.Controls.Add(mark1);

                MarkControl mark2 = new MarkControl();
                mark2.ForeColor = color;
                mark2.BackColor = Color.Transparent;
                mark2.Location = new Point(width - 30, posY);
                mark2.ContextMenuStrip = this.contextMenuStripMark;
                image.Controls.Add(mark2);

                //Line Struct contains the information for a single line
                Line line = new Line();
                line.mark1 = mark1;
                line.mark2 = mark2;
                line.Width = 1;


                //Events for moving marks
                mark1.MouseUp += new System.Windows.Forms.MouseEventHandler(this.Mark_MouseUp);
                mark1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Mark_MouseDown);
                mark1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.Mark_MouseMove);

                mark2.MouseUp += new System.Windows.Forms.MouseEventHandler(this.Mark_MouseUp);
                mark2.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Mark_MouseDown);
                mark2.MouseMove += new System.Windows.Forms.MouseEventHandler(this.Mark_MouseMove);

                //Adds Line object to an arraylist
                Lines.Add(line);
                Redraw();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private struct Line
        {
            public MarkControl mark1;
            public MarkControl mark2;
            public int Width;
        }

        //Simply draws a line
        private void DrawLine(Line line)
        {
            Graphics g = null;

            g = Graphics.FromImage(image.Image);
            g.SmoothingMode = SmoothingMode.AntiAlias;
            Color color = Properties.Settings.Default.LineColor;
            g.DrawLine(new Pen(color, (float)line.Width), line.mark1.Center.X, line.mark1.Center.Y, line.mark2.Center.X, line.mark2.Center.Y);
            g.Dispose();
        }

        //Redraws all the lines and a part of the background
        private void Redraw(Line line, Point p)
        {
            if (image != null && bmpBack != null)
            {
                Graphics.FromImage(image.Image).DrawImage(bmpBack, 0, 0, image.Image.Width,
                image.Image.Height);
            }

            foreach (Line l in Lines)
            {
                DrawLine(l);
            }

            Region r = getRegionByLine(line, p);
            image.Invalidate(r);
            image.Update();
        }


        //Redraws all the lines and the background too
        private void Redraw()
        {
            if (bmpBack != null)
                image.Image = (Bitmap)bmpBack.Clone();
            else
            {
                image.Image = new Bitmap(image.Width, image.Height);
                Graphics.FromImage(image.Image).Clear(Color.Transparent);
            }

            foreach (Line l in Lines)
            {
                DrawLine(l);
            }
            image.Refresh();
        }

        private void Mark_MouseDown(object sender, MouseEventArgs e)
        {
            this.SuspendLayout();
            isSelected = true;
            _X = e.X;
            _Y = e.Y;
        }

        private void Mark_MouseMove(object sender, MouseEventArgs e)
        {
            if (isSelected)
            {
                MarkControl mc1 = (MarkControl)sender;
                Line l = getLineByMark(mc1);

                Point p = new Point(e.X - _X + mc1.Left, e.Y - _Y + mc1.Top);

                mc1.Location = p;

                Redraw(l, p);
            }
        }

        private void Mark_MouseUp(object sender, MouseEventArgs e)
        {
            isSelected = false;
            ResumeLayout();
            Redraw();
        }

        //Retrieves a mark having the other one
        private MarkControl getOtherMark(MarkControl m)
        {
            foreach (Line l in Lines)
            {
                if (l.mark1 == m)
                    return l.mark2;

                if (l.mark2 == m)
                    return l.mark1;
            }//Never happens :D
            throw new Exception("No relative mark found");
        }

        //Retrieves a Line object having a mark
        private Line getLineByMark(MarkControl m)
        {
            foreach (Line l in Lines)
            {
                if (l.mark1 == m || l.mark2 == m)
                    return l;
            }//Never happens :D
            throw new Exception("No line found");
        }

        ////Shows Dialog to change line's width
        //private void Width_Click(object sender, System.EventArgs e)
        //{
        //    Line l;
        //    WDialog wd = new WDialog(getLineByMark((MarkControl)linecmenu.SourceControl).Width);
        //    DialogResult res = wd.ShowDialog();
        //    if (res == DialogResult.OK)
        //    {
        //        l = getLineByMark((MarkControl)linecmenu.SourceControl);
        //        Lines.Remove(l);
        //        l.Width = wd.LineWidth;
        //        Lines.Add(l);
        //        Redraw();
        //    }
        //}
        //Deletes line
        //private void Delete_Click(object sender, System.EventArgs e)
        //{
        //    Line l = getLineByMark(((MarkControl)linecmenu.SourceControl));
        //    Lines.Remove(l);
        //    l.mark1.Dispose();
        //    l.mark2.Dispose();
        //    linecmenu.SourceControl.Dispose();
        //    Redraw();
        //}

        //Returns the region to update
        private Region getRegionByLine(Line l, Point p)
        {
            GraphicsPath gp = new GraphicsPath();
            gp.AddPolygon(new Point[] { l.mark1.Center, l.mark2.Center, p, l.mark1.Center });

            RectangleF rf = gp.GetBounds();
            gp.Dispose();

            rf.Inflate(100f, 100f);

            return new Region(rf);
        }

        private void buttonColorChoice_Click(object sender, EventArgs e)
        {
            DialogResult result = colorDialog.ShowDialog();
            if (result == DialogResult.OK)
            {
                this.colorView.BackColor = colorDialog.Color;
            }
        }

        #endregion

        private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            Console.WriteLine("Reloading image...");
            string request = (string)e.Argument;
            StartCircle(this.loadingCircle);
            if (backgroundWorker.CancellationPending)
            {
                Console.WriteLine("Reloading image cancelled.");
                return;
            }
            Thread.Sleep(2000);
            if (backgroundWorker.CancellationPending)
            {
                Console.WriteLine("Reloading image cancelled.");
                return;
            }
            Communication com = new Communication();
            e.Result = com.comdirectImage(request, "");
        }

        private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            Console.WriteLine("Reloading image completed.");
            if (this.broker != null && e.Result != null && !backgroundWorker.CancellationPending)
            {
                this.broker.chartDetail = (Image)e.Result;

                this.bmpBack = (Bitmap)e.Result;
                this.image.Image = (Bitmap)bmpBack.Clone();
                this.image.SendToBack();

                this.image.Width = this.bmpBack.Width;
                this.image.Height = this.bmpBack.Height;

                this.Redraw();
            }
            
            this.loadingCircle.FadeOut();
        }

        private void StartCircle(LoadingCircle circle)
        {
            if (circle.InvokeRequired)
            {
                circle.Invoke(new StartCircleDelegate(StartCircle), new object[] { circle });
                return;
            }
            circle.FadeIn();
        }
        private delegate void StartCircleDelegate(LoadingCircle circle);

        private void StopCircle(LoadingCircle circle)
        {
            if (circle.InvokeRequired)
            {
                circle.Invoke(new StopCircleDelegate(StopCircle), new object[] { circle });
                return;
            }
            circle.FadeOut();
        }
        private delegate void StopCircleDelegate(LoadingCircle circle);

        private void button1_Click_1(object sender, EventArgs e)
        {
            this.loadingCircle.Active = true;
            this.loadingCircle.FadeIn();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            this.loadingCircle.FadeOut();
            this.loadingCircle.Active = false;
        }

        private void DetailView_FormClosing(object sender, FormClosingEventArgs e)
        {
            this.broker.ActiveDetailView = null;
            this.broker.AdditionalNotations.Clear();
        }

        private void deleteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ToolStripMenuItem item = (ToolStripMenuItem)sender;
            ToolStrip strip = (ToolStrip)item.Owner;

            Point stripLoc = this.image.PointToClient(strip.Location);
            int stripX = stripLoc.X;
            int stripY = stripLoc.Y;
            
            foreach (MarkControl control in this.image.Controls)
            {
                int markX = control.Center.X;
                int markY = control.Center.Y;

                if (Math.Abs(markX - stripX) < 4 && Math.Abs(markY - stripY) < 4)
                {
                    Line line = getLineByMark(control);
                    MarkControl mark2 = getOtherMark(control);
                    Lines.Remove(line);
                    this.image.Controls.Remove(control);
                    this.image.Controls.Remove(mark2);
                    Redraw();
                    break;
                }
            }
        }

        private void buttonSaveChart_Click(object sender, EventArgs e)
        {
            if (this.broker == null)
            {
                MessageBox.Show("Es wurde kein Objekt ausgewählt.");
                return;
            }
            SaveFileDialog dialog = new SaveFileDialog();
            dialog.FileName = DateTime.Now.ToFileTime() + "_" + this.broker.Symbol + ".png";
            DialogResult result = dialog.ShowDialog();
            if (result == DialogResult.OK)
            {
                Image img = this.image.Image;
                img.Save(dialog.FileName, ImageFormat.Png);
            }
        }

        private void buttonSetStandard_Click(object sender, EventArgs e)
        {
            Config.Broker.StandardChartOptions = this.number;
            Config.Save();
        }

        private void DetailView_SizeChanged(object sender, EventArgs e)
        {
            ChartOptions opt = Config.Broker.GetChartOptionsByNumber(this.number);
            if (opt == null)
            {
                opt = Config.Broker.GetStandardChartOptions(this.number);
            }
            if (opt != null)
            {
                opt.WindowWidth = this.Width;
                opt.WindowHeight = this.Height;
                Config.Save();
            }
        }

        private void trackBarTransparency_Scroll(object sender, EventArgs e)
        {
            int value = ((TrackBar)sender).Value * 10;
            foreach (Line line in Lines)
            {
                int color = line.mark1.BackColor.R;
                line.mark1.BackColor = Color.FromArgb(value, color, color, color);
                line.mark1.ForeColor = Color.FromArgb(value, color, color, color);
                line.mark2.BackColor = Color.FromArgb(value, color, color, color);
                line.mark2.ForeColor = Color.FromArgb(value, color, color, color);
            }
        }

        private void trackBarColor_Scroll(object sender, EventArgs e)
        {
            int value = ((TrackBar)sender).Value * 10;
            foreach (Line line in Lines)
            {
                int trans = line.mark1.BackColor.A;
                line.mark1.BackColor = Color.FromArgb(trans, value, value, value);
                line.mark1.ForeColor = Color.FromArgb(trans, value, value, value);
                line.mark2.BackColor = Color.FromArgb(trans, value, value, value);
                line.mark2.ForeColor = Color.FromArgb(trans, value, value, value);
            }
        }

        private void listViewComparison_ItemChecked(object sender, ItemCheckedEventArgs e)
        {
            //ListView lv = (ListView)sender;
            //if (lv.CheckedIndices.Count > 0)
            //{
            //    this.broker.AdditionalNotations.Clear();
            //    foreach (int index in lv.CheckedIndices)
            //    {
            //        BrokerObject broker = this.brokers[index];
            //        ListViewItem lvItem = lv.Items[index];
            //        this.broker.AdditionalNotations.Add(broker.IdNotation, lvItem.BackColor);
            //    }
            //    ReloadImage();
            //}
            //else
            //{
            //    this.broker.AdditionalNotations.Clear();
            //    ReloadImage();
            //}
        }

        private MessageForm waitForm = new MessageForm();

        private void backgroundWorkerReloadImage_DoWork(object sender, DoWorkEventArgs e)
        {
            object[] args = (object[]) e.Argument;
            BrokerObject broker = (BrokerObject)args[0];
            BackgroundWorker backgroundWorker = (BackgroundWorker)args[1];
            string request = ChartController.buildRequest(broker);
            DateTime startTime = DateTime.Now;
            e.Result = new object[] { startTime, request };
            if (backgroundWorker.IsBusy)
            {
                waitForm.Show();
                backgroundWorker.CancelAsync();
                while (backgroundWorker.IsBusy && (DateTime.Now - startTime).TotalSeconds < 30)
                {
                    Thread.Sleep(500);
                    this.Refresh();
                }
            }
        }

        private void backgroundWorkerReloadImage_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            object[] args = (object[])e.Result;
            DateTime startTime = (DateTime)args[0];
            waitForm.Close();
            string request = (string)args[1];
            if ((DateTime.Now - startTime).TotalSeconds < 30)
            {
                backgroundWorker.RunWorkerAsync(request);
            }
        }

    }
}
